/*
    This file is part of jTopaz.
    http://gcl.cis.udel.edu/projects/jtopaz
    Copyright (C) 2009 University of Delaware

    jTopaz is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    jTopaz is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of                                                        
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with jTopaz.  If not, see <http://www.gnu.org/licenses/>.
*/
function GridLoginComponent(){}

GridLoginComponent.prototype = {
    _gamaURI: null,
    _cert: null,
    _certPath: null,
    _dn: null,
    _username: null,
    _password: null,
    _certName: null,
    _spName: null,
    _profileDir: null,
    
    get gamaServiceURI()        { return this._gamaURI; },
    set gamaServiceURI( aURI)   { return this._gamaURI = aURI; },

    get username()        { return this._username; },
    set username( aUusername)   { return this._username = aUsername; },

    get password()        { return ""; },
    set password( aPass)   { return this._password = aPass; },

    get cert()        { return this._cert; },
    get certPath()        { return this._cert; },
    get dn()        { return this._dn; },
	get certName() {return this._certName; },
	set certName(cert) {return this._certName = cert; },
	get spName() {return this._spName; },
	set spName(signingPolicy) { return this._spName=signingPolicy; },

	get profileDir() {return this._profileDir; },
	set profileDir(dir) {return this._profileDir = dir; },

   /*
    * Choose the location of the GAMA web service to 
    * using an interactive xul interface.
    *
    *@returns true if success, false otherwise
    */
    selectGAMALocation: function()
    {
	//select the location via a xul interface
	var windowMediator = Components.classes['@mozilla.org/appshell/window-mediator;1']
                              .getService(Components.interfaces.nsIWindowMediator);

	var win = windowMediator.getMostRecentWindow( "navigator:browser" );
    

	var reg = new Object();
	 	win.openDialog("chrome://sample/content/gamaSelect.xul", "_blank",
                        "scrollbars,resizable,chrome,modal=yes,dialog=yes", reg);
	if( reg.newRegistry != null )
	{
	    this._gamaURI = reg.newRegistry;
	    return true;
	}

	return false;

    },

   /*
    * Enter username/password for GAMA.
    * Any values entered will be stored in the component
    * for use by other functions
    *@returns true if both values entered, false otherwise
    */
    promptUsernameAndPassword: function( )
    {


	const PROMPTSERVICE_CONTRACTID = "@mozilla.org/embedcomp/prompt-service;1";
	const nsIPromptService = Components.interfaces.nsIPromptService;
	this.promptService = Components.classes[PROMPTSERVICE_CONTRACTID].getService(nsIPromptService);
	
	var usr = {value:""};
	var pass = {value:""};
	var check = {value:true};

	var okorcancel = this.promptService.promptUsernameAndPassword(null, 'Login Information', 'Enter Username and Password', usr, pass, null, check );

	if(  !usr.value || !pass.value )
	    return false;

	this._username = usr.value;
	this._password = pass.value;
	
	return true;

    },


   /*
    * Call GAMA service to retrieve proxy cert and dn
    *@param usr - [optional] username to login with. 
    *		overrides any previously set username via promptUsernameAndPassword
    *@param pass - [optional] password to login with. 
    *		overrides any previously set password via promptUsernameAndPassword
    *@param loc - [optional] URI of GAMA service 
    *		overrides any previously set password via selectGAMALocation
    *@returns true if successful login, false otherwise
    */
    doLogin: function ( usr, pass, loc) 
    { 

	var gama_uri;

	if( (loc != null) &&  (loc != "" ) )
	    gama_uri = loc;
	else if( (this._gamaURI != null) &&  (this._gamaURI != "" ) )
	    gama_uri = this._gamaURI;
	else return false;

	var port_index  = gama_uri.lastIndexOf( "." );
	
	var temp_gama_uri = gama_uri.substring( 0, port_index + 4 );
	gama_uri = temp_gama_uri + ":9090/axis/services/SecurityMyproxyloginService";

	var k  = gama_uri.lastIndexOf( "/" );
	var list_service_uri  = gama_uri.substring( 0,  k + 1 );
 
	var ioService = Components.classes["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService);

	//this is where i added it:
	const PROMPTSERVICE_CONTRACTID = "@mozilla.org/embedcomp/prompt-service;1";
	const nsIPromptService = Components.interfaces.nsIPromptService;
	this.promptService = Components.classes[PROMPTSERVICE_CONTRACTID].getService(nsIPromptService);
	
	
	var ca_uri = ioService.newURI(list_service_uri, null, null);
	//this.promptService.alert(null, 'test', ca_uri.spec);
	var ca_url = 'http://' + ca_uri.hostPort;
	ca_url += "/cacerts/";
	dump(ca_url);
	dump('\n');	
	list_service_uri += "SecurityUserImportService";
	

	var call = Components.classes["@mozilla.org/xmlextras/soap/call;1"]
			    .createInstance(Components.interfaces.nsISOAPCall);

	
	call.transportURI = gama_uri;
	    
	this.promptService.alert(null, call, call);

	var param1 = Components.classes["@mozilla.org/xmlextras/soap/parameter;1"]
			    .createInstance(Components.interfaces.nsISOAPParameter);
	param1.name="username";


	var username = "";


	//this is where it was:
	/*
	const PROMPTSERVICE_CONTRACTID = "@mozilla.org/embedcomp/prompt-service;1";
	const nsIPromptService = Components.interfaces.nsIPromptService;
	this.promptService = Components.classes[PROMPTSERVICE_CONTRACTID].getService(nsIPromptService);
	*/
	if( (usr!= null) && (usr!="") ) 
	    username = usr;
	else if( (this._username != null) && (this._username != "") )
	{
	    username = this._username;
	}
	else{
	  this.promptService.alert(null, 'Invalid Login', 'A username and password must be entered');
		return false;
	}

	    param1.value = username;
	// throw error if no username/password
		    

	var param2 = Components.classes["@mozilla.org/xmlextras/soap/parameter;1"]
			    .createInstance(Components.interfaces.nsISOAPParameter);
	param2.name="passwd";
	
	if( (pass != null) && (pass !="") ) 
	    password = pass;
	else if( (this._password != null) && (this._password != "") )
	    password= this._password;
	else{
	  this.promptService.alert(null, 'Invalid Login', 'A username and password must be entered');
	 	return false;
	}
	    param2.value = password;
	// throw error if no username/password
		    
	//this.promptService.alert(null, call, call);
	var paramArray = new Array( param1, param2 );
	call.encode(0, "loginUserMyProxy", "urn:axis", 0, null, paramArray.length, paramArray );
	alert(call.message);
	
	var ser = Components.classes["@mozilla.org/xmlextras/xmlserializer;1"]
			    .createInstance(Components.interfaces.nsIDOMSerializer);
	//this.promptService.alert(null, "idk", ser);
	this.promptService.alert(null, "envelope", ser.serializeToString(call.envelope));
	this.promptService.alert(null, "message", ser.serializeToString(call.message));
	this.promptService.alert(null, "header", ser.serializeToString(call.header));
	this.promptService.alert(null, "body", ser.serializeToString(call.body));
	
	//Display error message if gama server cannot be found. Either bad address or server is down
	try{
		var ret = call.invoke();
	}catch( e ){
	  this.promptService.alert(null, 'Gama Server Error', 'The specified gama server could not be found.');
	 	return false;
	}				

	// if no loginUserMyProxyReturn, login has failed, inform user
	var nlist = ret.message.getElementsByTagName("loginUserMyProxyReturn");
	if( nlist.length == 0 )
	{
	  this.promptService.alert(null, 'Invalid Login Information', 'The username or password you entered is not valid');
	 	return false;
	  //throw  "Incorrect Username/Password. \n Try Again";
	}
      
	// parse out the actual cert from the body to return
	var cert = ret.message.getElementsByTagName("loginUserMyProxyReturn").item(0).firstChild.nodeValue;

	this._cert = cert;
	
	dump("cert\n" + cert);    
	// get the DN 
	var listCall = Components.classes["@mozilla.org/xmlextras/soap/call;1"]
			    .createInstance(Components.interfaces.nsISOAPCall);
	listCall.transportURI = list_service_uri;
	listCall.encode(0, "listUsers", "urn:axis", 0, null, new Array().length, new Array() );

	var listRet = listCall.invoke();

	// users holds dn,username pairs
	// firefox has a 4k limit on how large a dom Text element can be 
	// Mozilla Bug# 194231
	var listUsersReturn =  listRet.message.getElementsByTagName("listUsersReturn").item(0);
	var users = "";
	if( listUsersReturn.childNodes.length > 1 )
	{
	    for( var i = 0; i < listUsersReturn.childNodes.length; i++ )
	    {
		users += listUsersReturn.childNodes.item(i).nodeValue;
	    }
	}


	var domParser = Components.classes["@mozilla.org/xmlextras/domparser;1"]
			    .createInstance(Components.interfaces.nsIDOMParser);

	var usersDoc = domParser.parseFromString(users,"text/xml");

	var nodes = this.xPathSearch( usersDoc ,  '//user[username=\"'+username+'"]');

	
	//user->userDN
	this._dn  = nodes[0].childNodes.item(3).firstChild.nodeValue;


  var profDir = Components.classes["@mozilla.org/file/directory_service;1"].getService(Components.interfaces.nsIProperties)
                .get("ProfD", Components.interfaces.nsILocalFile);

	this._profileDir = profDir.path;
  profDir.append( "topaz" );
	var caDir = profDir;
	var caPath = caDir.path;
       
  profDir.append( "x509_proxy" );

	var proxyPath = profDir.path;

	var env = Components.classes["@mozilla.org/process/environment;1"].getService( Components.interfaces.nsIEnvironment);
	env.set( "X509_USER_PROXY", proxyPath );

	var ca_env = Components.classes["@mozilla.org/process/environment;1"].getService( Components.interfaces.nsIEnvironment);
	env.set( "X509_CERT_DIR", caPath );



	var proxyFile = Components.classes["@mozilla.org/file/local;1"]
                     .createInstance(Components.interfaces.nsILocalFile);



	proxyFile.initWithPath( proxyPath );
	//this.promptService.alert(null, proxyPath, proxyPath);

	try{
	    // file is nsIFile, data is a string
	    var foStream = Components.classes["@mozilla.org/network/file-output-stream;1"]
				     .createInstance(Components.interfaces.nsIFileOutputStream);

	    // use 0x02 | 0x10 to open file for appending.
	    foStream.init( proxyFile , 0x02 | 0x08 | 0x20, 0600, 0); // write, create, truncate
	    foStream.write( this._cert , this._cert.length);
	    foStream.close();
	}catch( e ){ 
		//this.promptService.alert(null, e, e);
		return false; }




	// save the cert to a place in the profile or to ~/.globus/certificates

//Make an xmlhttprequest in order to obtain the CA certificate and signing policy
	dump("httprequest: \n");
	dump(ca_url);
	var http_req = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"]
											.createInstance(Components.interfaces.nsIXMLHttpRequest);
	http_req.open("GET",ca_url,false);
	http_req.send(null);
	if(http_req.status == 200){
		var match = /\w{8}\.signing\_policy/.exec(http_req.responseText);
		var signing_policy = String(match);
		var ca_cert =signing_policy.substring(0, 8) + ".0";
		var ca_req = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"]
											.createInstance(Components.interfaces.nsIXMLHttpRequest);
		ca_req.open("GET",ca_url+ca_cert,false);
		ca_req.send(null);
		if(ca_req.status == 200)
		{
			var ca_cert_file = String(ca_req.responseText);
			var caFilePath= caPath + "/" + ca_cert;
			var caFile = Components.classes["@mozilla.org/file/local;1"]
                     .createInstance(Components.interfaces.nsILocalFile);
			caFile.initWithPath( caFilePath );
			//this.promptService.alert(null, "cert", caFile.leafName);
			this._certName = caFile.leafName;
			try{
	    		// file is nsIFile, data is a string
	    		var caStream = Components.classes["@mozilla.org/network/file-output-stream;1"]
				     .createInstance(Components.interfaces.nsIFileOutputStream);
	    		// use 0x02 | 0x10 to open file for appending.
	    		caStream.init( caFile , 0x02 | 0x08 | 0x20, 0600, 0); // write, create, truncate
					caStream.write( ca_cert_file , ca_cert_file.length);
					caStream.close();
			}catch( e ){ return false; }
		}
		else{
			dump(ca_req.status);
	  	this.promptService.alert(null, 'CA Certificate Error', 'An error occured while trying to obtain the ca certificate');
	 		return false;
		}
		var signing_policy_req = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"]
											.createInstance(Components.interfaces.nsIXMLHttpRequest);
		signing_policy_req.open("GET",ca_url+signing_policy,false);
		signing_policy_req.send(null);
		if(signing_policy_req.status == 200)
		{
			var signing_policy_file = signing_policy_req.responseText;
			
			//creating path to write file, need to find way to create copy
			//of directory listing so that no "/" has to be added, rather
			//just append the name
			var spFilePath= caPath + "/" + signing_policy; 
			var spFile = Components.classes["@mozilla.org/file/local;1"]
                     .createInstance(Components.interfaces.nsILocalFile);

			spFile.initWithPath( spFilePath );
						this._spName=spFile.leafName;
			//this.promptService.alert(null, "test", this._spName);

			try{
	    		// file is nsIFile, data is a string
	    		var foStream = Components.classes["@mozilla.org/network/file-output-stream;1"]
				     .createInstance(Components.interfaces.nsIFileOutputStream);

	    		// use 0x02 | 0x10 to open file for appending.
	    		foStream.init( spFile , 0x02 | 0x08 | 0x20, 0600, 0); // write, create, truncate
	    		foStream.write( signing_policy_file , signing_policy_file.length);
	    		foStream.close();
			}catch( e ){ return false; }
		}
		else{
			dump(signing_policy_req.status);
	  	this.promptService.alert(null, 'Signing Policy Error', 'An error occured while trying to obtain the signing policy');
	 		return false;
		}
	}
	else{
		dump("Error loading page\n");
	}
	return true; 

    },

    
    xPathSearch: function(aXmlDoc, aXpath)
    {
      var pXPE = Components.classes["@mozilla.org/dom/xpath-evaluator;1"]
		    .createInstance(Components.interfaces.nsIDOMXPathEvaluator);

	var foundNodes = new Array();
	var result = pXPE.evaluate(aXpath, aXmlDoc, pXPE.createNSResolver(aXmlDoc.documentElement), 0, null);

	while (res = result.iterateNext())
	{
	    foundNodes.push(res);
	}

	return foundNodes;
    },


	QueryInterface: function (iid) {
	    if (!iid.equals(Components.interfaces.nsIGridLogin)
            && !iid.equals(Components.interfaces.nsISupports))
        {
            throw Components.results.NS_ERROR_NO_INTERFACE;
        }
        return this;
    }

}

var Module = {
    firstTime: true,

    registerSelf: function (compMgr, fileSpec, location, type) {
        if (this.firstTime) {
            dump("*** Deferring registration of GridLogin JS components\n");
            this.firstTime = false;
            throw Components.results.NS_ERROR_FACTORY_REGISTER_AGAIN;
        }
        debug("*** Registering JS component\n");
        compMgr =
compMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar);
        compMgr.registerFactoryLocation(this.myCID,
                                        "GridLogin JS Component",
                                        this.myProgID,
                                        fileSpec,
                                        location,
                                        type);
    },

    getClassObject : function (compMgr, cid, iid) {
        if (!cid.equals(this.myCID))
        throw Components.results.NS_ERROR_NO_INTERFACE
        if (!iid.equals(Components.interfaces.nsIFactory))
        throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
        return this.myFactory;
    },

    myCID: Components.ID("{7014C1D4-A123-4D00-B066-18B3F3E531B0}"),
//0x7014C1D4, 0xA123, 0x4D00, { 0xB0, 0x66, 0x18, 0xB3, 0xF3, 0xE5, 0x31, 0xB0 }
    myProgID: "@mozilla.org/js_grid_login_component;1",

    myFactory: {
        createInstance: function (outer, iid) {
            dump("CI: " + iid + "\n");
            if (outer != null)
            throw Components.results.NS_ERROR_NO_AGGREGATION;
            return (new GridLoginComponent()).QueryInterface(iid);
        }
    },

    canUnload: function(compMgr) {
        dump("****** Unloading: GridLogin JS component! ****** \n");
        return true;
    }
}; // END Module

function NSGetModule(compMgr, fileSpec) { return Module; }
